﻿using System;
using System.Linq;
using RayDen.Library.Components.Surface;
using RayDen.Library.Core;
using RayDen.Library.Core.Primitives;
using RayDen.RayEngine.Core.Interface;
using RayDen.RayEngine.Core.Types;
using RayDen.RayEngine.Data;

namespace RayDen.RayEngine.VertexFramework
{


    [Flags]
    public enum PathVertexType : byte
    {
        Environment = 0,
        Geometry = 1,
        Lightsource = 2,
        Volume = 3,
        Specular = 4,

        EyeSuperNode = 8,
        EyeNode = 16,
        SurfaceNode = 32,
        LightNode=64,
        LightSuperNode=128,

        SpecularGeometry = Specular | Geometry,
        DiffuseGeometry = Geometry,
        DiffuseLightsource = Lightsource,
        SpecularLightsource = Lightsource | Specular,
        Camera = 7,

    }
    public struct PathVertexData
    {
        public Point HitPoint;
        public Point DpDu, DpDv;
        public Normal GeoNormal, ShadingNormal;
        public Normal DnDu, DnDv;
        public UV TexCoords;
    }


    public class PathEdge
    {
        public Vector Dir;
        public RgbSpectrum Contribution;
        public float Length, Pdf;
    }

    public abstract class PathElement
    {
        public Vector WIncident, WOutgoing;
        public float Weight;
        public SurfaceIntersectionData HitInfo;
        public abstract PathVertexType Type { get; }

        public virtual float Pdf(ref Vector wi, ref Vector wo)
        {
            return 0f;
        }

        public virtual PathElement SampleNext(float[] w, out PathEdge edge)
        {
            throw new NotImplementedException();
        }
    }

    public class EyeSuperNode : PathElement
    {
        public float ImageX, ImageY, Time;
        public override PathVertexType Type
        {
            get { return PathVertexType.EyeSuperNode; }
        }

        public override PathElement SampleNext(float[] w, out PathEdge edge)
        {
            edge = new PathEdge();
            return this;
        }
    }



    public class EyeNode : PathElement
    {
        public Vector Direction;
        public float U, V;
        public override PathVertexType Type
        {
            get { return PathVertexType.EyeNode; }
        }
    }



    public class ObserverPathElement : PathElement
    {
        public CameraSample CameraSample;
        public override PathVertexType Type
        {
            get { return PathVertexType.Camera; }
        }
    }

    public class EnvironmentPathElement : PathElement
    {
        public RgbSpectrum Throughput;
        public override PathVertexType Type
        {
            get { return PathVertexType.Environment; }
        }
    }


    public class LightsourcePathElement : PathElement
    {
        public bool Delta;
        public LightSample LightSample;
        public RgbSpectrum Throughput;
        public override PathVertexType Type
        {
            get { return PathVertexType.DiffuseLightsource; }
        }
    }

    public class GeometryPathElement : PathElement
    {
        public BsdfSample BsdfSample;

        public Point HitPoint;

        public override PathVertexType Type
        {
            get
            {
                return PathVertexType.Geometry;

                return BsdfSample.Bounce.HasFlag(BxDFType.Diffuse) ?
                                  PathVertexType.DiffuseGeometry :
                                  BsdfSample.Bounce.HasFlag(BxDFType.Specular) || BsdfSample.Bounce.HasFlag(BxDFType.Diffuse) ?
                                      PathVertexType.SpecularGeometry : PathVertexType.Geometry;
            }
        }
    }

    public static class IntersectionTypeExtensions
    {
        public static string ToPathString(this PathVertexType t)
        {
            switch (t)
            {
                case PathVertexType.Environment:
                    return "A";
                case PathVertexType.DiffuseGeometry:
                    return "D";
                case PathVertexType.DiffuseLightsource:
                    return "L";
                case PathVertexType.Camera:
                    return "E";
                case PathVertexType.SpecularGeometry:
                    return "S";

            }
            return string.Empty;
        }
    }

    public class PathVertex : ISerializableValue
    {
        public Point HitPoint;   //12
        public float RayDistance;    //+4 = 16
        public Normal GeoNormal, ShadingNormal;//+24 = 40
        public UV TexCoords; //+8 = 48
        public Vector IncomingDirection, OutgoingDirection; //+24 = 72
        public PathVertexType HitType; //+1 = 73
        public ISurfaceMaterial Material;//+8 = 81
        public float BsdfPdf, rrWeight;//+4 = 85
        public RgbSpectrum Throughput; //+12 = 97
        public float SampleU, SampleV, SampleW; // 12 = 109

        public string VertexType
        {

            get
            {
                var tc = this;
                return PathExpressionHelper.GetVertexExpression(ref tc);
            }
        }

        public PathVertex()
        {
            rrWeight = 1f;
        }

        public byte[] Serialize()
        {
            return new[]
                {
                    HitPoint.Serialize(),
                    BitConverter.GetBytes(RayDistance),
                    GeoNormal.Serialize(),
                    ShadingNormal.Serialize(),
                    TexCoords.Serialize(),
                    IncomingDirection.Serialize(),
                    OutgoingDirection.Serialize(),
                    new []{ (byte)HitType},
                    Material.Name.GetBytes(),
                    BitConverter.GetBytes(BsdfPdf),
                    BitConverter.GetBytes(rrWeight),
                    Throughput.Serialize(),
                    BitConverter.GetBytes(SampleU),
                    BitConverter.GetBytes(SampleV),
                    BitConverter.GetBytes(SampleW)
                }.SelectMany(t => t).ToArray();
        }

        public void Deserialize(byte[] data, int offset = 0)
        {
            throw new NotImplementedException();
        }
    }


    public class PathLightVertex
    {
        public ILight Light;
        public Point  HitPoint;
        public Vector LightSourceDirection; // Sampled direction
        public RgbSpectrum Radiance;

        public float Weight;
    }
}
